<template>
  <div ref="terminalContainer" class="terminal-container"></div>
</template>

<script setup>
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
import 'xterm/css/xterm.css';

const emit = defineEmits(['data'])
const terminalContainer = ref(null)

const term = ref(null)
const fitAddon = ref(null) // 改为 ref 形式
let resizeObserver = null
let isDestroyed = ref(false) // 新增销毁状态标记

const initializeTerminal = async () => {
  if (isDestroyed.value) return//增加销毁状态检查
  if (term.value?.element) {//1.确保之前实例已清理
    try {term.value.dispose()
    } catch(e) {console.error('Resize error:', e)}
    term.value.element.remove()
  }
  term.value = new Terminal({
    cursorBlink: true,
    scrollback: 1000,
    allowProposedApi: true
  })
  try {   //确保 addon 初始化顺序
    fitAddon.value = new FitAddon() // 使用 ref 管理
    term.value.loadAddon(fitAddon.value) // 先加载
    term.value.open(terminalContainer.value) // 后打开
  }catch(e) {
    console.error('Addon load error:', error)
    return
  }
  // 添加 DOM 存在性检查
  await nextTick(() => {    // 等待DOM更新完成
    if (!terminalContainer.value?.offsetParent) return
    setupResizeObserver()    // 设置尺寸监听
    handleResize()    // 初始化自适应// fitAddon.fit()
  })
  //处理用户输入
  term.value.onData(data => emit('data', data))
}
const handleResize = () => {
  try {
    if (fitAddon.value && term.value) {// 修改4：增加存在性检查
      fitAddon.value.fit()
      term.value.scrollToBottom()
    }
  } catch (error) {
    console.error('Resize error:', error)
  }
}
const setupResizeObserver = () => {
  resizeObserver = new ResizeObserver(entries => {
    for (let entry of entries) {
      if (entry.contentRect) handleResize()
    }
  })
  if (terminalContainer.value) {
    resizeObserver.observe(terminalContainer.value)
  }
}
onMounted(() => {
  initializeTerminal();
  window.addEventListener('resize', handleResize)
})
onBeforeUnmount(() => {
  isDestroyed.value = true // 标记已销毁
  const cleanup = () => { //优化清理顺序
    if (resizeObserver) {// 清理顺序很重要：先移除监听器
      resizeObserver.disconnect()
      resizeObserver = null
    }
    window.removeEventListener('resize', handleResize)
    if(term.value){
      try {//强制清理 DOM 元素
        term.value.element?.remove()
        term.value.textarea?.remove()
      } catch {}
      try {// 不要手动处理 fitAddon！
        term.value.dispose() // 这会自动清理所有已加载的 addon
      } catch (e) {// 核心清理逻辑
        console.warn('Dispose warning:', e)
        if (term.value._core) {
          term.value._core.viewport?.dispose()
          term.value._core.screenElement?.remove()
        }
      }
      term.value = null
    }
    fitAddon.value = null
  }
  //使用双 requestAnimationFrame 确保清理时序
  requestAnimationFrame(() => {
    requestAnimationFrame(cleanup)
  })
})

// 暴露方法
const write = (data) => term.value.write(data)
const clear = () => term.value.clear()
defineExpose({ write, clear })
</script>


<style scoped>
@import 'xterm/css/xterm.css';
.terminal-container {
  transition: opacity 0.2s;   /* 过渡防止闪烁 */
  width: 100%;
  height: 100%;
  min-height: 300px; /* 设置最小高度 */
  overflow: hidden;
  background: #1e1e1e; /* 保持终端背景色一致 */
}

</style>


<!--在模板里有一个div作为容器，样式里设置了height:100%。初始化终端的时候，加载了fitAddon，并且调用了fit()方法。-->
<!--自动调整大小通常涉及监听容器尺寸变化，然后初始化调用fitAddon.fit()。，但之后没有处理窗口或者容器大小变化的事件。-->
<!--在onMounted的时候初始化终端，这时候执行了一次fit()。但如果容器的大小随后发生变化，比如父元素是响应式的，或者窗口被调整，终端不会自动适应。-->
<!--这时候就需要添加resize事件监听，在窗口变化时重新调用fit()。-->
<!--用户可能在初始化时调用fitAddon.fit()，但有时候可能需要等容器完全渲染后再调用，比如使用nextTick。确保容器尺寸已经计算-->

